#include "agi4c.h"
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>

KEYVAL *request[MAX_REQUEST];
char *channel = "";

char *substring(char *string, int position, int length) {
  char *pointer;
  int c;
  pointer = malloc(length + 1);
  for (c = 0; c < position; c++) {
    string++;
  }
  for (c = 0; c < length; c++) {
    *(pointer + c) = *string;
    string++;
  }
  *(pointer + c) = '\0';
  return pointer;
}

char *trim(char *src) {
  char *s = substring(src, 0, strlen(src));
  int i = 0;
  int j = strlen(s) - 1;
  int k = 0;

  while (isspace(s[i]) && s[i] != '\0') {
    i++;
  }

  while (isspace(s[j]) && j >= 0) {
    j--;
  }

  while (i <= j) {
    s[k++] = s[i++];
  }

  s[k] = '\0';

  return s;
}

char *trim_mask(char *src, char *m) {
  char *s = substring(src, 0, strlen(src));
  int i = 0;
  int j = strlen(s) - 1;
  int k = 0;

  while ((isspace(s[i]) || strchr(m, s[i]) != NULL)  && s[i] != '\0') {
    i++;
  }

  while ((isspace(s[j]) || strchr(m, s[j]) != NULL) && j >= 0) {
    j--;
  }

  while (i <= j) {
    s[k++] = s[i++];
  }

  s[k] = '\0';

  return s;
}

char *agi_request(char *name) {
  char *tag = NULL;
  KEYVAL *kv;
  int no = 0;

  for (no = 0; no < MAX_REQUEST; no++) {
    if (request[no] == NULL) {
      break;
    }
    kv = request[no];
    if (strcmp(name, kv->key) == 0) {
      tag = kv->val;
      break;
    }   
  }  

  return tag;
}

void agi_collect_request() {
  char *line;
  int no = 0;
  for (no = 0; no < MAX_REQUEST; no++) {
    request[no] = NULL;
  }
  no = 0;
  line = agi_readline();
  while (line != NULL) {
    if (strlen(line) == 0) {
      break;
    }
    if (strcmp("\n", line) == 0) {
      break;
    }
    char *pch = strchr(line, ':');
    if (pch == NULL) {
      continue;
    }
    int pos = pch - line;
    KEYVAL *kv = (KEYVAL *)malloc(sizeof(KEYVAL));
    kv->key = substring(line, 0, pos);
    char *val = substring(line, pos + 1, strlen(line) - pos - 1);
    if (strlen(val) > 0) {
      val = substring(val, 1, strlen(val) - 1);
    }
    kv->val = val;
    request[no] = kv;
    no++;
    line = agi_readline();
  }
  request[no] = NULL;
} 

char *agi_readline() {
  int maxsize = 1024;
  char *tag = (char *)malloc(maxsize + 1);
  if (fgets(tag, maxsize, stdin) != NULL) {
    int len = strlen(tag);
    if (len > 0) {
      tag[len - 1] = '\0';
    }
    return tag;
  } else {
    return NULL;
  }
}

char *agi_readstring() {
  int maxsize = 10240;
  char *tag = (char *)malloc(maxsize + 1);
  if (fgets(tag, maxsize, stdin) != NULL) {
    return tag;
  } else {
    return NULL;
  }
}

void agi_set_channel(char *src) {
  channel = substring(src, 0, strlen(src));
}

AGI_RESULT *agi_answer() {
  return agi_evaluate("ANSWER");
}

AGI_RESULT *agi_hangup(char *channel) {
  char buf[10240];
  sprintf(buf, "HANGUP %s", channel);
  return agi_evaluate(buf);
}

AGI_RESULT *agi_evaluate(char *command) {
  AGI_RESULT *tag = (AGI_RESULT *)malloc(sizeof(AGI_RESULT));
  tag->channel = substring(channel, 0, strlen(channel));
  tag->command = substring(command, 0, strlen(command));
  tag->code = 500;
  tag->data = "";
  tag->more = "";
  tag->result = "";

  fprintf(stdout, "%s\n", command);
  fflush(stdout);
	
  char *line = agi_readline();
  char *cph = strchr(line, ' ');
  if (cph != NULL) {
    int pos = cph - line;
    char *code = substring(line, 0, pos);
    char *str = trim(substring(line, pos, strlen(line) - pos));
    tag->code = atoi(code);
	tag->data = str;
	cph = strchr(str, ' ');
	if (cph != NULL) {
		char *rs = substring(str, 0, (cph - str));
		cph = strchr(rs, '=');
		if (cph != NULL) {
		  char *key = substring(rs, 0, (cph - rs));
		  char *val = substring(rs, (cph - rs) + 1, strlen(rs) - (cph - rs));
		  if (strcmp("result", key) == 0) {
		    tag->result = trim(val);
		  }
		}
	}
  } else {
    tag->data = line;
  }

  return tag;
}

AGI_RESULT *agi_exec(char *application, char *options) {
  char buf[10240];
  sprintf(buf, "EXEC %s %s", application, options);
  return agi_evaluate(buf);
}

AGI_RESULT *agi_exec_dial(char *type, char *identifier, char *timeout, char *options, char *url) {
  char buf[10240];
  sprintf(buf,  "%s/%s,%s,%s,%s", type, identifier, timeout, options, url);
  return agi_exec("Dial", trim_mask(buf, ","));
}

AGI_RESULT *agi_stream_file(char *filename, char *escape_digits) {
  char buf[10240];
  sprintf(buf, "STREAM FILE %s \"%s\" 0", filename, escape_digits);
  return agi_evaluate(buf);
}

AGI_RESULT *agi_text2wave(char *text, char *escape_digits) {
  char temp[1024];
  tmpnam(temp);

  char txtfile[1024];
  char wavfile[1024];
  sprintf(txtfile, "%s.txt", temp);
  sprintf(wavfile, "%s.wav", temp);
  
  FILE *fp = fopen(txtfile, "a");
  fputs(text, fp);
  fclose(fp);
  
  char cmd[10240];
  sprintf(cmd, "/usr/bin/text2wave -f 8000 -o \"%s\" \"%s\" ", wavfile, txtfile);
  system(cmd);
  
  AGI_RESULT *tag = agi_stream_file(temp, escape_digits);
  tag->more = substring(text, 0, strlen(text));
  
  remove(wavfile);
  remove(txtfile);
  
  return tag;
}

AGI_RESULT *agi_mp3togsm(char *filename, char *escape_digits) {
  char temp[1024];
  tmpnam(temp);

  char gsmfile[1024];
  char wavfile[1024];
  sprintf(gsmfile, "%s.gsm", temp);
  sprintf(wavfile, "%s.wav", temp);
  
  char cmd[10240];
  sprintf(cmd, "/usr/bin/lame --decode \"%s\" \"%s\" ", filename, wavfile);
  system(cmd);
  sprintf(cmd, "/usr/bin/sox -V \"%s\" -r 8000 -c 1 \"%s\" resample -ql", wavfile, gsmfile);
  system(cmd);

  remove(wavfile);
  
  AGI_RESULT *tag = agi_stream_file(temp, escape_digits);
  
  remove(gsmfile);
  
  return tag;
}

char *agi_mp3togsm_file(char *filename) {
  char temp[1024];
  tmpnam(temp);

  char gsmfile[1024];
  char wavfile[1024];
  sprintf(gsmfile, "%s.gsm", temp);
  sprintf(wavfile, "%s.wav", temp);
  
  char cmd[10240];
  sprintf(cmd, "/usr/bin/lame --decode \"%s\" \"%s\" ", filename, wavfile);
  system(cmd);
  sprintf(cmd, "/usr/bin/sox -V \"%s\" -r 8000 -c 1 \"%s\" resample -ql", wavfile, gsmfile);
  system(cmd);

  remove(wavfile);
  
  return substring(temp, 0, strlen(temp));
}

AGI_RESULT *agi_vnspeak(char *textfile, char *escape_digits) {
  char temp[1024];
  tmpnam(temp);

  char mp3file[1024];
  sprintf(mp3file, "%s.mp3", temp);

  char cmd[10240];
  sprintf(cmd, "/usr/bin/vnspeak \"%s\" \"%s\" ", textfile, mp3file);
  system(cmd);

  AGI_RESULT *tag = agi_mp3togsm(mp3file, escape_digits);
  
  remove(mp3file);
  
  return tag;
}

AGI_RESULT *agi_play_mp3(char *mp3file) {
  return agi_exec("MP3Player", mp3file);
}

AGI_RESULT *agi_vnspeak_viqr(char *text, char *escape_digits) {
  char temp[1024];
  tmpnam(temp);

  char viqrfile[1024];
  char utf8file[1024];
  sprintf(viqrfile, "%s.viqr.txt", temp);
  sprintf(utf8file, "%s.utf8.txt", temp);

  FILE *fp = fopen(viqrfile, "a");
  fputs(text, fp);
  fclose(fp);
  
  char cmd[10240];
  sprintf(cmd, "/usr/bin/viqr2utf8 \"%s\" \"%s\" ", viqrfile, utf8file);
  system(cmd);

  remove(viqrfile);
  
  AGI_RESULT *tag = agi_vnspeak(utf8file, escape_digits);
  
  remove(utf8file);
  
  return tag;
}

char *agi_vnspeak_file(char *textfile) {
  char temp[1024];
  tmpnam(temp);

  char mp3file[1024];
  sprintf(mp3file, "%s.mp3", temp);

  char cmd[10240];
  sprintf(cmd, "/usr/bin/vnspeak \"%s\" \"%s\" ", textfile, mp3file);
  system(cmd);

  char *tag = agi_mp3togsm_file(mp3file);
  
  remove(mp3file);
  
  return tag;
}

char *agi_vnspeak_viqr_file(char *text) {
  char temp[1024];
  tmpnam(temp);

  char viqrfile[1024];
  char utf8file[1024];
  sprintf(viqrfile, "%s.viqr.txt", temp);
  sprintf(utf8file, "%s.utf8.txt", temp);

  FILE *fp = fopen(viqrfile, "a");
  fputs(text, fp);
  fclose(fp);
  
  char cmd[10240];
  sprintf(cmd, "/usr/bin/viqr2utf8 \"%s\" \"%s\" ", viqrfile, utf8file);
  system(cmd);

  remove(viqrfile);
  
  char *tag = agi_vnspeak_file(utf8file);
  
  remove(utf8file);
  
  return tag;
}

AGI_RESULT *agi_get_data(char *filename, char *timeout, char *max_digits) {
  char buf[10240];
  sprintf(buf, "GET DATA \"%s\" %s %s", filename, timeout, max_digits);
  return agi_evaluate(trim(buf));
}

char *agi_text_input(char *mode) {
  char tag[1024];
  char tmp[1024];
  char bc[2];
  tmp[0] = '\0';
  tag[0] = '\0';
  bc[0] = '\0';
  bc[1] = '\0';
  char c = '\0';
  
  do {
	AGI_RESULT *res = agi_get_data("beep", "", "");
	int i = 0;
	int n = 0;
	c = '\0';
	for (i = 0; i < strlen(res->result); i++) {
		char nc = res->result[i];
		if (nc == '*') {
			if (n > 0) {
  			  bc[0] = keypress(c, n, mode);
			  if (c != '*' && c != '\0') {
			    strcat(tag, bc);
			    strcat(tmp, bc);
			  }
			}
			n = 0;
			c = nc;
			break;
		}
		if (c == nc) {
		  n++;
		} else {
			if (n > 0) {
  			  bc[0] = keypress(c, n, mode);
			  if (c != '*' && c != '\0') {
			    strcat(tag, bc);
			    strcat(tmp, bc);
			  }
			}
			n = 1;
			c = nc;
		}
	}
	if (n > 0) {
		bc[0] = keypress(c, n, mode);
		if (c != '*' && c != '\0') {
		  strcat(tag, bc);
		  strcat(tmp, bc);
		}
	}
	if (strlen(tmp) > 0) {
	   agi_say_punctuation(tmp, "*");
	}
	tmp[0] = '\0';
  } while (c != '*');
  if (strlen(tag) > 0) {
    agi_say_punctuation(tag, "*");
  }
  return substring(tag, 0, strlen(tag));
}

AGI_RESULT *agi_say_punctuation(char *text, char *escape_digits) {
    char buf[1024];
	buf[0] = '\0';
	int i = 0;
    for (i = 0; i < strlen(text); i++) {
		char *ret;
        switch (text[i]) {
            case ' ': ret = "SPACE "; break;
            case ',': ret = "COMMA "; break;
            case '.': ret = "PERIOD "; break;
            case '?': ret = "QUESTION MARK "; break;
            case '!': ret = "EXPLANATION POINT "; break;
            case ':': ret = "COLON "; break;
            case ';': ret = "SEMICOLON "; break;
            case '#': ret = "POUND "; break;
            case '=': ret = "EQUALS "; break;
            case '<': ret = "LESS THAN "; break;
            case '(': ret = "LEFT PARENTHESIS "; break;
            case '[': ret = "LEFT BRACKET "; break;
            case '{': ret = "LEFT BRACE "; break;
            case '@': ret = "AT "; break;
            case '$': ret = "DOLLAR SIGN "; break;
            case '&': ret = "AMPERSAND "; break;
            case '%': ret = "PERCENT "; break;
            case '>': ret = "GREATER THAN "; break;
            case ')': ret = "RIGHT PARENTHESIS "; break;
            case ']': ret = "RIGHT BRACKET "; break;
            case '}': ret = "RIGHT BRACE "; break;
            case '+': ret = "PLUS "; break;
            case '-': ret = "MINUS "; break;
            case '*': ret = "ASTERISK "; break;
            case '/': ret = "SLASH "; break;
            case '\'': ret = "SINGLE QUOTE "; break;
            case '`': ret = "BACK TICK "; break;
            case '"': ret = "QUOTE "; break;
            case '^': ret = "CAROT "; break;
            case '\\': ret = "BACK SLASH "; break;
            case '|': ret = "BAR "; break;
            case '_': ret = "UNDERSCORE "; break;
            case '~': ret = "TILDE "; break;
            default:
				ret = malloc(3);
				ret[0] = text[i];
				ret[1] = ' ';
				ret[2] = '\0';
				break;
        }
		strcat(buf, ret);
    }
    return agi_text2wave(buf, escape_digits);
}

char keypress(char c, int n, char *mode) {
	char tag = '\0';
	char *s[10];
	char *k[10];
	s[0] = "=";
	s[1] = "<([{1";
	s[2] = "@$&%2";
	s[3] = ">)]}3";
	s[4] = "+-*/4";
	s[5] = "'`5";
	s[6] = "\"6";
	s[7] = "^7";
	s[8] = "\\|8";
	s[9] = "_~9";
	k[0] = " ,.?0";
	k[1] = "!:;#1";
	k[2] = "ABC2";
	k[3] = "DEF3";
	k[4] = "GHI4";
	k[5] = "JKL5";
	k[6] = "MNO6";
	k[7] = "PQRS7";
	k[8] = "TUV8";
	k[9] = "WXYZ9";

	if (c < '0' || c > '9') {
		return tag;
	}
	
	if (strcmp("SYMBOL", mode) == 0) {
		int m = c - '0';
		if (m >= 0 && m <= 9) {
			char *t = s[m];
			if (n <= strlen(t)) {
				tag = t[n - 1];
			}
		}
	} else if (strcmp("UPPERCASE", mode) == 0 || strcmp("LOWERCASE", mode) == 0) {
		int m = c - '0';
		if (m >= 0 && m <= 9) {
			char *t = k[m];
			if (n <= strlen(t)) {
				tag = t[n - 1];
				if (strcmp("LOWERCASE", mode) == 0) {
					tag = tolower(tag);
				}
			}
		}
	} else {
		if (c >= '0' && c <= '9') {
		  tag = c;
		}
	}
	return tag;
}

char *agi_text2wave_file(char *text) {
  char temp[1024];
  tmpnam(temp);

  char txtfile[1024];
  char wavfile[1024];
  sprintf(txtfile, "%s.txt", temp);
  sprintf(wavfile, "%s.wav", temp);
  
  FILE *fp = fopen(txtfile, "a");
  fputs(text, fp);
  fclose(fp);
  
  char cmd[10240];
  sprintf(cmd, "/usr/bin/text2wave -f 8000 -o \"%s\" \"%s\" ", wavfile, txtfile);
  system(cmd);
    
  remove(txtfile);
  
  return substring(temp, 0, strlen(temp));
}

KEYVAL **agi_makechoicelist(int choice_count) {
  KEYVAL **tag = (KEYVAL **)malloc(choice_count * sizeof(KEYVAL *));
  return tag;
}

void agi_addchoice(KEYVAL **choicelist, int no, char *key, char *prompt) {
	choicelist[no] = (KEYVAL *)malloc(sizeof(KEYVAL));
	choicelist[no]->key = substring(key, 0, strlen(key));
	choicelist[no]->val = substring(prompt, 0, strlen(prompt));
}

char *agi_menu(KEYVAL **choicelist, int choice_count) {
	char keys[1024];
	char **filelist;
	keys[0] = '\0';
	filelist = (char **)malloc(choice_count * sizeof(char *));
	int i = 0;
	for (i = 0; i < choice_count; i++) {
		strcat(keys, choicelist[i]->key);
		if (choicelist[i]->val[0] == '*') {
			filelist[i] = agi_text2wave_file(substring(choicelist[i]->val, 1, strlen(choicelist[i]->val) - 1));
		} else if (choicelist[i]->val[0] == '#') {
			filelist[i] = agi_vnspeak_file(substring(choicelist[i]->val, 1, strlen(choicelist[i]->val) - 1));
		} else if (choicelist[i]->val[0] == '~') {
			filelist[i] = agi_vnspeak_viqr_file(substring(choicelist[i]->val, 1, strlen(choicelist[i]->val) - 1));
		} else if (choicelist[i]->val[0] == '!') {
			filelist[i] = agi_mp3togsm_file(substring(choicelist[i]->val, 1, strlen(choicelist[i]->val) - 1));
		} else {
			filelist[i] = substring(choicelist[i]->val, 0, strlen(choicelist[i]->val));
		}
	}
	char *choice = NULL;
    while (choice == NULL) {
		AGI_RESULT *ret;
		for (i = 0; i < choice_count; i++) {
			ret = agi_stream_file(filelist[i], keys);
			if (ret->code != AGIRES_OK || strcmp("-1", ret->result) == 0) {
				choice = "-1";
				break;
			}
			if (strcmp("0", ret->result) != 0) {
				int c = atoi(ret->result);
				char tmp[1024];
				sprintf(tmp, "%c", c);
				choice = substring(tmp, 0, strlen(tmp));
				break;
			}
		}
		if (choice == NULL) {
			ret = agi_get_data("beep", "2000", "1");
			if (ret->code != AGIRES_OK || strcmp("-1", ret->result) == 0) {
				choice = "-1";
			} else if (strlen(ret->result) > 0) {
				int c = atoi(ret->result);
				char tmp[1024];
				sprintf(tmp, "%c", c);
				char *cph = strchr(keys, tmp[0]);
				if (cph != NULL) {
					choice = substring(tmp, 0, strlen(tmp));
				}
			}
		}
	}
	for (i = 0; i < choice_count; i++) {
		remove(filelist[i]);
	}
    return substring(choice, 0, 1);
}

char *agi_get_variable(char *name) {
	char buf[1024];
	sprintf(buf, "GET VARIABLE %s", name);
	AGI_RESULT *res = agi_evaluate(buf);
	char *tag = substring(res->data, 0, strlen(res->data));
	char *cph = strchr(tag, '(');
	if (cph != NULL) {
		tag = substring(tag, (cph - tag + 1), strlen(tag) - (cph - tag + 1));
	}
	cph = strrchr(tag, ')');
	if (cph != NULL) {
		tag = substring(tag, 0, (cph - tag));
	}
	return tag;
}